<template>
  <view class="image-upload">
    <!-- 上传区域 -->
    <view 
      class="upload-zone"
      :class="{
        'upload-zone--disabled': disabled || isUploading,
        'upload-zone--grid': layout === 'grid'
      }"
      @click="layout === 'list' ? handleClickUpload : null"
    >
      <!-- 网格布局：显示已上传的图片 -->
      <template v-if="layout === 'grid'">
        <view class="image-grid">
          <!-- 已上传的图片 -->
          <view 
            v-for="(image, index) in completedFiles" 
            :key="image.id" 
            class="image-item"
          >
            <view class="image-wrapper">
              <image 
                :src="image.previewUrl || image.url" 
                :alt="image.name" 
                class="image-preview"
                mode="aspectFit"
                @click.stop="previewImage(image, index)"
              />
              <view class="image-overlay">
                <view class="image-actions">
                  <view class="action-btn action-preview" @click.stop="previewImage(image, index)">
                    <uni-icons type="eye" size="16" color="#fff"></uni-icons>
                  </view>
                  <view class="action-btn action-download" @click.stop="downloadImage(image)">
                    <uni-icons type="download" size="16" color="#fff"></uni-icons>
                  </view>
                  <view class="action-btn action-delete" @click.stop="removeImage(index)">
                    <uni-icons type="trash" size="16" color="#fff"></uni-icons>
                  </view>
                </view>
              </view>
              <view class="image-name">{{ image.name }}</view>
            </view>
          </view>

          <!-- 上传按钮（如果未达到最大数量） -->
          <view 
            v-if="completedFiles.length < maxCount" 
            class="upload-placeholder"
          >
            <button 
              class="upload-btn"
              :disabled="disabled || isUploading"
              @click="chooseImages"
            >
              <uni-icons type="plus" size="32" color="#c0c4cc"></uni-icons>
              <text class="upload-text">{{ uploadText }}</text>
            </button>
          </view>
        </view>
      </template>

      <!-- 列表布局：传统上传区域 -->
      <template v-else>
        <view class="upload-zone__content">
          <uni-icons type="image" size="48" color="#c0c4cc"></uni-icons>
          <view class="upload-zone__text">
            <text class="upload-zone__title">{{ uploadText }}</text>
            <text class="upload-zone__subtitle">
              支持 {{ allowedTypes.join('、') }} 格式，单个文件不超过 {{ maxFileSize }}MB
              <template v-if="enableCompress">
                <template v-if="compressMode === 'quality'">
                  ，上传时会自动质量压缩（质量: {{ (compressQuality * 100).toFixed(0) }}%）
                </template>
                <template v-else>
                  ，上传时会自动压缩至 {{ maxFileSizeKb }}KB 左右
                </template>
              </template>
            </text>
            <text class="upload-zone__subtitle" v-if="maxCount > 1">
              最多可上传 {{ maxCount }} 张图片
            </text>
          </view>
        </view>
      </template>
    </view>

    <!-- 列表布局：统一的文件列表 -->
    <view v-if="layout === 'list'" class="unified-file-list">
      <!-- 操作栏 -->
      <view class="list-header">
        <text>图片列表 ({{ allFiles.length }} 个文件)</text>
        <view class="header-actions">
          <button 
            v-if="!isUploading && hasWaitingFiles" 
            class="btn btn-primary btn-sm" 
            @click.stop="startUpload"
          >
            开始上传
          </button>
          <button 
            v-if="isUploading" 
            class="btn btn-danger btn-sm" 
            @click.stop="cancelUpload"
          >
            取消上传
          </button>
          <button 
            v-if="allFiles.length > 0" 
            class="btn btn-text btn-sm" 
            @click.stop="clearAll"
          >
            清空全部
          </button>
        </view>
      </view>

      <!-- 统一文件列表 -->
      <view v-if="allFiles.length > 0" class="file-section">
        <view class="file-list">
          <view 
            v-for="(file, index) in allFiles" 
            :key="file.id" 
            class="file-item"
            :class="{ 'file-item--error': file.status === 'error' }"
          >
            <!-- 左侧：图片预览 -->
            <view class="file-image">
              <view class="image-preview-container">
                <image 
                  v-if="file.previewUrl || file.url" 
                  :src="file.previewUrl || file.url" 
                  :alt="file.name" 
                  class="preview-image" 
                  :class="{ 'clickable': file.status === 'completed' }"
                  mode="aspectFit"
                  @click.stop="file.status === 'completed' ? previewImageFromList(file, index) : null"
                />
                <view v-else class="image-placeholder">
                  <uni-icons type="image" size="48" color="#c0c4cc"></uni-icons>
                  <text class="placeholder-text">图片预览</text>
                </view>

                <!-- 状态覆盖层 -->
                <view v-if="file.status === 'uploading'" class="upload-overlay">
                  <progress 
                    :percent="Math.round(file.progress)" 
                    stroke-width="4"
                    activeColor="#409eff"
                  ></progress>
                  <text class="upload-progress">{{ Math.round(file.progress) }}%</text>
                </view>
              </view>
            </view>

            <!-- 右侧：文件信息和操作 -->
            <view class="file-content">
              <!-- 文件信息 -->
              <view class="file-info">
                <text class="file-name">{{ file.name }}</text>
                <view class="file-meta">
                  <view class="meta-item">
                    <text class="meta-label">大小：</text>
                    <text class="meta-value">
                      {{ formatFileSize(file.size) }}
                      <template v-if="file.isCompressed">
                        <text class="compress-info">
                          (原始: {{ formatFileSize(file.originalSize) }}, 
                          压缩: {{ file.compressRatio }}%)
                        </text>
                      </template>
                    </text>
                  </view>
                  <view v-if="file.status !== 'completed'" class="meta-item">
                    <text class="meta-label">方式：</text>
                    <text class="meta-value">
                      {{ file.uploadMethod === 'chunk' ? '分片上传' : '普通上传' }}
                      <text v-if="file.supportDirectUpload" class="direct-tag">直传</text>
                    </text>
                  </view>
                  <view v-else-if="file.filePath" class="meta-item">
                    <text class="meta-label">路径：</text>
                    <text class="meta-value file-path">{{ file.filePath }}</text>
                  </view>
                </view>
              </view>

              <!-- 状态和操作 -->
              <view class="file-actions">
                <!-- 状态标签 -->
                <view class="status-section">
                  <view v-if="file.status === 'waiting'" class="status-tag waiting">等待上传</view>
                  <view v-else-if="file.status === 'uploading'" class="status-tag uploading">上传中</view>
                  <view v-else-if="file.status === 'completed'" class="status-tag completed">已完成</view>
                  <view v-else-if="file.status === 'error'" class="status-tag error">上传失败</view>
                  <view v-if="file.isCompressed" class="status-tag compressed">已压缩</view>
                </view>

                <!-- 操作按钮 -->
                <view class="action-buttons">
                  <template v-if="file.status === 'completed'">
                    <button class="btn-sm btn-primary" @click.stop="previewImageFromList(file, index)">
                      预览
                    </button>
                    <button class="btn-sm btn-info" @click.stop="copyPath(file.filePath)">
                      复制路径
                    </button>
                    <button class="btn-sm btn-success" @click.stop="downloadImage(file)">
                      下载
                    </button>
                  </template>
                  <template v-else-if="file.status === 'error'">
                    <button class="btn-sm btn-warning" @click.stop="retryUpload(file)">
                      重试
                    </button>
                  </template>

                  <button 
                    v-if="file.status !== 'uploading'" 
                    class="btn-sm btn-danger" 
                    @click.stop="removeFileFromList(file, index)" 
                    :disabled="isUploading && file.status !== 'error'"
                  >
                    删除
                  </button>
                </view>
              </view>

              <!-- 错误信息 -->
              <view v-if="file.status === 'error' && file.errorMessage" class="error-message">
                <view class="error-alert">
                  <uni-icons type="info" size="16" color="#f56c6c"></uni-icons>
                  <text>{{ file.errorMessage }}</text>
                </view>
              </view>
            </view>
          </view>
        </view>
      </view>
    </view>

    <!-- 图片预览组件 -->
    <ImagePreview
      v-if="showPreview"
      :src-list="completedFiles.map(f => f.previewUrl || f.url)"
      :current-index="currentPreviewIndex"
      @close="closePreview"
      @change="onPreviewChange"
    />
  </view>
</template>

<script>
import { createUploadManager, formatFileSize, generateFileId, generatePreviewUrl } from '@/utils/upload-utils'
import ImagePreview from '@/components/ImagePreview/index.vue'

// #ifndef H5
// 小程序和APP环境使用 watasi-compress
import WCompress from '@/uni_modules/watasi-compress/js_sdk/index.js'
// #endif

export default {
  name: 'ImageUpload',
  components: {
    ImagePreview
  },
  props: {
    // 最大图片数量
    maxCount: {
      type: Number,
      default: 5
    },
    // 单个文件最大大小(MB)
    maxFileSize: {
      type: Number,
      default: 10
    },
    // 允许的图片类型
    allowedTypes: {
      type: Array,
      default: () => ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']
    },
    // 布局方式: 'list' | 'grid'
    layout: {
      type: String,
      default: 'grid',
      validator: (value) => ['list', 'grid'].includes(value)
    },
    // 默认存储配置ID
    defaultConfigId: {
      type: Number,
      default: null
    },
    // 默认上传目录
    defaultDir: {
      type: String,
      default: 'images'
    },
    // 是否禁用
    disabled: {
      type: Boolean,
      default: false
    },
    // 分片大小(MB)
    chunkSize: {
      type: Number,
      default: 5
    },
    // 预设图片列表 - 支持数组或字符串（逗号分隔）
    modelValue: {
      type: [Array, String],
      default: () => []
    },
    // 是否自动开始上传
    autoUpload: {
      type: Boolean,
      default: true
    },
    // 是否启用图片压缩
    enableCompress: {
      type: Boolean,
      default: false
    },
    // 压缩模式: 'quality'(质量压缩) | 'size'(大小压缩)
    compressMode: {
      type: String,
      default: 'quality',
      validator: (value) => ['quality', 'size'].includes(value)
    },
    // 压缩质量 (0.1-1)
    compressQuality: {
      type: Number,
      default: 0.8,
      validator: (value) => value >= 0.1 && value <= 1
    },
    // 目标文件大小（KB），仅在 size 模式下使用
    maxFileSizeKb: {
      type: Number,
      default: 50,
      validator: (value) => value >= 10 && value <= 2048
    },
    // 压缩后最小文件大小限制（KB），小于此值不进行压缩
    minCompressSize: {
      type: Number,
      default: 50
    }
  },
  emits: ['update:modelValue', 'upload-success', 'upload-error', 'upload-progress', 'preview', 'remove', 'compress-start', 'compress-end'],
  data() {
    return {
      isDragOver: false,
      isUploading: false,
      allFiles: [],
      storageConfigs: [],
      selectedConfigId: this.defaultConfigId,
      uploadDir: this.defaultDir,
      dynamicMaxFileSize: this.maxFileSize,
      uploadManager: null,
      
      // 预览相关
      showPreview: false,
      currentPreviewIndex: 0,
      
      
      // 防止递归更新的标志
      isInternalUpdate: false
    }
  },
  computed: {
    uploadText() {
      if (this.disabled) return '上传已禁用'
      if (this.isUploading) return '上传中...'
      if (this.layout === 'grid' && this.completedFiles.length >= this.maxCount) {
        return '已达到最大数量'
      }
      return '点击选择图片上传'
    },
    hasWaitingFiles() {
      return this.allFiles.some(file => file.status === 'waiting')
    },
    // 已完成的文件（用于网格模式显示和数量限制）
    completedFiles() {
      return this.allFiles.filter(file => file.status === 'completed')
    }
  },
  watch: {
    modelValue: {
      handler(newVal) {
        if (!this.isInternalUpdate) {
          this.initFromModelValue()
        }
      },
      deep: true,
      immediate: true
    },
    completedFiles: {
      handler() {
        this.isInternalUpdate = true
        this.updateModelValue()
        this.$nextTick(() => {
          this.isInternalUpdate = false
        })
      },
      deep: true
    },
    defaultConfigId(newVal) {
      if (newVal && newVal !== this.selectedConfigId) {
        this.selectedConfigId = newVal
        this.updateDynamicMaxFileSize()
      }
    },
    defaultDir(newVal) {
      this.uploadDir = newVal
    },
    selectedConfigId() {
      this.updateDynamicMaxFileSize()
    }
  },
  created() {
    this.initUploadManager()
    this.initFromModelValue()
  },
  mounted() {
    // 总是尝试加载存储配置，确保组件可以正常工作
    this.loadStorageConfigs()
  },
  methods: {
    formatFileSize,

    // 获取文件扩展名
    getFileExtension(filePath) {
      if (!filePath) return 'jpg'
      const parts = filePath.split('.')
      return parts.length > 1 ? parts.pop().toLowerCase() : 'jpg'
    },

    // 更改文件扩展名为 .jpg
    changeFileExtToJpg(fileName) {
      if (!fileName) return 'image.jpg'
      const nameWithoutExt = fileName.replace(/\.[^.]+$/, '')
      return `${nameWithoutExt}.jpg`
    },

    // H5 环境的 Canvas 压缩实现
    // #ifdef H5
    compressImageH5(file, quality, width) {
      return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = (e) => {
          const img = new Image()
          img.onload = () => {
            const canvas = document.createElement('canvas')
            const ctx = canvas.getContext('2d')
            
            // 计算压缩后的尺寸
            let targetWidth = width || img.width
            let targetHeight = img.height
            
            if (width && img.width > width) {
              targetWidth = width
              targetHeight = Math.round((img.height * width) / img.width)
            }
            
            canvas.width = targetWidth
            canvas.height = targetHeight
            
            // 绘制图片
            ctx.fillStyle = '#FFFFFF'
            ctx.fillRect(0, 0, canvas.width, canvas.height)
            ctx.drawImage(img, 0, 0, targetWidth, targetHeight)
            
            // 转换为 Blob
            canvas.toBlob((blob) => {
              if (blob) {
                resolve(blob)
              } else {
                reject(new Error('压缩失败'))
              }
            }, 'image/jpeg', quality)
          }
          img.onerror = () => reject(new Error('图片加载失败'))
          img.src = e.target.result
        }
        reader.onerror = () => reject(new Error('文件读取失败'))
        reader.readAsDataURL(file.file || file)
      })
    },
    // #endif

    // 使用 watasi-compress 插件进行图片压缩（小程序/APP）或 Canvas 压缩（H5）
    compressImage(file) {
      return new Promise((resolve, reject) => {
        // 如果文件小于最小压缩大小，直接返回原文件
        if (file.size < this.minCompressSize * 1024) {
          resolve(file)
          return
        }

        // 大小压缩模式：如果原始文件已经小于或接近目标大小，直接跳过
        if (this.compressMode === 'size' && file.size <= this.maxFileSizeKb * 1024 * 1.1) {
          resolve(file)
          return
        }

        // 获取文件路径
        const filePath = file.path || file.tempFilePath
        if (!filePath) {
          console.error('无法获取文件路径')
          resolve(file)
          return
        }

        // 开始压缩（保留简单日志用于调试）
        // console.log(`开始压缩: ${file.name} (${(file.size/1024).toFixed(1)}KB)`)

        // 根据压缩模式选择压缩策略
        if (this.compressMode === 'quality') {
          // 质量压缩：直接使用指定质量压缩一次
          this.compressWithQuality(file, filePath, this.compressQuality, resolve)
        } else if (this.compressMode === 'size') {
          // 大小压缩：迭代压缩直到达到目标大小
          this.compressToTargetSize(file, filePath, resolve)
        }
      })
    },

    // 质量压缩：使用指定质量压缩一次
    async compressWithQuality(originalFile, filePath, quality, resolve) {
      // 根据质量参数智能计算合适的宽度
      let width = null
      
      if (quality <= 0.2) {
        width = 600
      } else if (quality <= 0.4) {
        width = 1000
      } else if (quality <= 0.6) {
        width = 1500
      } else if (quality <= 0.8) {
        width = 2000
      }
      
      // console.log(`质量压缩: 质量${(quality * 100).toFixed(0)}%, 宽度${width || '原始'}px`)
      
      // #ifdef H5
      // H5 环境使用自己的 Canvas 压缩
      try {
        const blob = await this.compressImageH5(originalFile, quality, width)
        
        // 检查压缩效果
        if (blob.size >= originalFile.size * 0.9) {
          resolve(originalFile)
          return
        }
        
        // 转换为 DataURL
        const reader = new FileReader()
        reader.onload = (e) => {
          const compressedFile = {
            ...originalFile,
            name: this.changeFileExtToJpg(originalFile.name), // 压缩后改为 .jpg
            path: e.target.result,
            tempFilePath: e.target.result,
            size: blob.size,
            type: 'image/jpeg',
            originalSize: originalFile.size,
            compressRatio: ((originalFile.size - blob.size) / originalFile.size * 100).toFixed(1),
            isCompressed: true,
            _blob: blob
          }
          
          // console.log(`H5压缩完成: ${compressedFile.name} (${compressedFile.compressRatio}%)`)
          
          resolve(compressedFile)
        }
        reader.readAsDataURL(blob)
      } catch (error) {
        console.error('H5 压缩失败:', error)
        resolve(originalFile)
      }
      // #endif
      
      // #ifndef H5
      // 小程序/APP 使用 watasi-compress
      const compressOptions = {
        quality: quality
      }
      
      if (width) {
        compressOptions.width = width
      }
      
      WCompress([filePath], {
        ...compressOptions,
        success: (compressedPaths) => {
          this.handleCompressResult(originalFile, filePath, compressedPaths, resolve)
        },
        fail: (error) => {
          console.error('watasi-compress 压缩失败:', error)
          resolve(originalFile)
        }
      })
      // #endif
    },

    // 大小压缩：迭代压缩直到达到目标大小
    async compressToTargetSize(originalFile, originalFilePath, resolve) {
      const targetSizeBytes = this.maxFileSizeKb * 1024
      const originalSizeKB = (originalFile.size / 1024).toFixed(1)
      
      // 根据原始文件大小和目标大小，智能计算初始质量和尺寸
      const sizeRatio = originalFile.size / targetSizeBytes
      let currentQuality
      let currentWidth = null
      
      // #ifdef H5
      // H5 的 Canvas 压缩更激进，使用适中的初始质量，并配合大步进快速收敛
      if (sizeRatio > 50) {
        currentQuality = 0.4
        currentWidth = 1000
      } else if (sizeRatio > 20) {
        currentQuality = 0.5
        currentWidth = 1200
      } else if (sizeRatio > 10) {
        currentQuality = 0.6
        currentWidth = 1500
      } else if (sizeRatio > 5) {
        currentQuality = 0.7
        currentWidth = 1800
      } else if (sizeRatio > 2) {
        currentQuality = 0.75
      } else {
        currentQuality = 0.8
      }
      // #endif
      
      // #ifndef H5
      // 小程序/APP 优化后的策略（10步内）
      if (sizeRatio > 50) {
        currentQuality = 0.35
        currentWidth = 900
      } else if (sizeRatio > 20) {
        currentQuality = 0.45
        currentWidth = 1100
      } else if (sizeRatio > 10) {
        currentQuality = 0.55
        currentWidth = 1300
      } else if (sizeRatio > 5) {
        currentQuality = 0.65
        currentWidth = 1600
      } else if (sizeRatio > 2) {
        currentQuality = 0.75
      } else {
        currentQuality = 0.85
      }
      // #endif
      
      let attempts = 0
      let maxAttempts
      
      // #ifdef H5
      maxAttempts = 12  // H5 使用大步进，12次足够
      // #endif
      // #ifndef H5
      maxAttempts = 15  // 小程序/APP 也使用大步进，15次足够
      // #endif
      
      let lastCompressedFile = null
      let bestFile = null
      let bestFileDiff = Infinity

      // console.log(`大小压缩: 原始${originalSizeKB}KB → 目标${this.maxFileSizeKb}KB`)

      const tryCompress = async () => {
        try {
          let compressedFile
          
          // #ifdef H5
          // H5 环境使用自己的 Canvas 压缩
          const blob = await this.compressImageH5(originalFile, currentQuality, currentWidth)
          
          // 转换为文件对象
          const reader = new FileReader()
          const dataUrl = await new Promise((res, rej) => {
            reader.onload = (e) => res(e.target.result)
            reader.onerror = () => rej(new Error('读取失败'))
            reader.readAsDataURL(blob)
          })
          
          compressedFile = {
            ...originalFile,
            name: this.changeFileExtToJpg(originalFile.name), // 压缩后改为 .jpg
            path: dataUrl,
            tempFilePath: dataUrl,
            size: blob.size,
            type: 'image/jpeg',
            originalSize: originalFile.size,
            compressRatio: ((originalFile.size - blob.size) / originalFile.size * 100).toFixed(1),
            isCompressed: true,
            _blob: blob
          }
          // #endif
          
          // #ifndef H5
          // 小程序/APP 使用 watasi-compress
          const compressOptions = {
            quality: currentQuality
          }
          
          if (currentWidth) {
            compressOptions.width = currentWidth
          }
          
          const compressedPaths = await new Promise((res, rej) => {
            WCompress([originalFilePath], {
              ...compressOptions,
              success: (paths) => res(paths),
              fail: (error) => rej(error)
            })
          })
          
          if (!compressedPaths || compressedPaths.length === 0) {
            resolve(bestFile || lastCompressedFile || originalFile)
            return
          }
          
          const compressedResult = compressedPaths[0]
          
          // 转换为统一格式
          compressedFile = await new Promise((res) => {
            this.getCompressedFileSize(compressedResult, originalFile, res)
          })
          // #endif
          
          // 处理压缩结果（两个平台都会执行）
          attempts++
          lastCompressedFile = compressedFile

          const currentSizeKB = (compressedFile.size / 1024).toFixed(1)
          const targetKB = this.maxFileSizeKb
          const sizeDiff = Math.abs(compressedFile.size - targetSizeBytes)
          
          // 记录最接近目标的文件
          if (compressedFile.size <= targetSizeBytes && sizeDiff < bestFileDiff) {
            bestFile = compressedFile
            bestFileDiff = sizeDiff
          }
          
          // 只在关键节点输出日志
          // console.log(`尝试 ${attempts}: ${currentSizeKB}KB / ${targetKB}KB`)

          // 判断是否达到目标或需要继续压缩
          let minAcceptableSize, minQuality, minWidth
          
          // #ifdef H5
          // H5 更宽松的判断：50%-100% 都接受
          minAcceptableSize = targetSizeBytes * 0.5
          minQuality = 0.3
          minWidth = 600
          // #endif
          
          // #ifndef H5
          // 小程序/APP 稍微宽松的判断：60%-100%
          minAcceptableSize = targetSizeBytes * 0.6
          minQuality = 0.25
          minWidth = 500
          // #endif
          
          // 计算与目标的误差百分比
          const errorPercent = Math.abs((compressedFile.size - targetSizeBytes) / targetSizeBytes) * 100
          
          if (compressedFile.size <= targetSizeBytes && compressedFile.size >= minAcceptableSize) {
            // 完美范围内，直接成功
            console.log(`✅ 压缩成功: ${compressedFile.name} ${originalSizeKB}KB → ${currentSizeKB}KB`)
            resolve(compressedFile)
          } else if (compressedFile.size <= targetSizeBytes * 1.2 && errorPercent <= 20) {
            // 在目标 ±20% 范围内都可以接受
            console.log(`✅ 压缩完成: ${compressedFile.name} ${originalSizeKB}KB → ${currentSizeKB}KB`)
            resolve(compressedFile)
          } else if (attempts >= maxAttempts) {
            // 达到最大尝试次数，使用最佳结果
            const resultFile = bestFile || lastCompressedFile
            const resultSizeKB = (resultFile.size / 1024).toFixed(1)
            console.log(`✅ 压缩完成: ${resultFile.name} ${originalSizeKB}KB → ${resultSizeKB}KB (目标${targetKB}KB)`)
            resolve(resultFile)
          } else if (compressedFile.size < minAcceptableSize) {
            // 压缩过度了（太小了），需要提高质量接近目标
            // const minTargetKB = (minAcceptableSize / 1024).toFixed(1)
            // console.log(`压缩过度，提高质量: ${currentSizeKB}KB < ${minTargetKB}KB`)
            
            // 计算需要提高多少质量
            const sizeRatio = targetSizeBytes / compressedFile.size
            let qualityIncrease
            
            if (sizeRatio > 3) {
              qualityIncrease = 0.15  // 大幅提高
            } else if (sizeRatio > 2) {
              qualityIncrease = 0.1
            } else if (sizeRatio > 1.5) {
              qualityIncrease = 0.06
            } else {
              qualityIncrease = 0.03
            }
            
            currentQuality = Math.min(0.95, currentQuality + qualityIncrease)
            
            // 同时适当增大尺寸
            if (currentWidth && sizeRatio > 1.5) {
              currentWidth = Math.min(2500, Math.round(currentWidth * 1.2))
            }
            
            setTimeout(() => tryCompress(), 100)
          } else if (currentQuality <= minQuality && (!currentWidth || currentWidth <= minWidth)) {
            // 质量和尺寸都已经很小，使用最佳结果
            const resultFile = bestFile || lastCompressedFile
            const resultSizeKB = (resultFile.size / 1024).toFixed(1)
            console.log(`✅ 压缩完成: ${resultFile.name} ${originalSizeKB}KB → ${resultSizeKB}KB`)
            resolve(resultFile)
          } else {
            // 继续压缩，智能调整质量和尺寸
            const currentSizeRatio = compressedFile.size / targetSizeBytes
            
            // #ifdef H5
            // H5 使用大步进策略，快速收敛（10步内）
            if (currentSizeRatio > 5 && currentWidth && currentWidth > 600) {
              // 超大差距：同时大幅调整尺寸和质量
              currentWidth = Math.max(600, Math.round(currentWidth * 0.6))
              currentQuality = Math.max(0.3, currentQuality - 0.1)
            } else if (currentSizeRatio > 3) {
              // 大差距
              if (currentWidth && currentWidth > 700) {
                currentWidth = Math.max(600, Math.round(currentWidth * 0.7))
              }
              currentQuality = Math.max(0.3, currentQuality - 0.08)
            } else if (currentSizeRatio > 2) {
              // 中等差距
              if (currentWidth && currentWidth > 800) {
                currentWidth = Math.max(600, Math.round(currentWidth * 0.8))
              }
              currentQuality = Math.max(0.3, currentQuality - 0.06)
            } else if (currentSizeRatio > 1.5) {
              // 较小差距
              currentQuality = Math.max(0.3, currentQuality - 0.04)
              if (currentWidth && currentWidth > 700) {
                currentWidth = Math.max(600, currentWidth - 100)
              }
            } else if (currentSizeRatio > 1.2) {
              // 接近目标
              currentQuality = Math.max(0.3, currentQuality - 0.03)
            } else {
              // 非常接近，微调
              currentQuality = Math.max(0.3, currentQuality - 0.01)
            }
            // #endif
            
            // #ifndef H5
            // 小程序/APP 也使用大步进策略（10步内）
            if (currentSizeRatio > 5 && currentWidth && currentWidth > 500) {
              // 超大差距：同时大幅调整
              currentWidth = Math.max(500, Math.round(currentWidth * 0.65))
              currentQuality = Math.max(0.25, currentQuality - 0.08)
            } else if (currentSizeRatio > 3) {
              // 大差距
              if (currentWidth && currentWidth > 600) {
                currentWidth = Math.max(500, Math.round(currentWidth * 0.75))
              }
              currentQuality = Math.max(0.25, currentQuality - 0.07)
            } else if (currentSizeRatio > 2) {
              // 中等差距
              if (currentWidth && currentWidth > 700) {
                currentWidth = Math.max(500, Math.round(currentWidth * 0.8))
              }
              currentQuality = Math.max(0.25, currentQuality - 0.06)
            } else if (currentSizeRatio > 1.5) {
              // 较小差距
              currentQuality = Math.max(0.25, currentQuality - 0.05)
              if (currentWidth && currentWidth > 600) {
                currentWidth = Math.max(500, currentWidth - 100)
              }
            } else if (currentSizeRatio > 1.2) {
              // 接近目标
              currentQuality = Math.max(0.25, currentQuality - 0.03)
            } else {
              // 非常接近，微调
              currentQuality = Math.max(0.25, currentQuality - 0.02)
            }
            // #endif
            
            setTimeout(() => tryCompress(), 100)
          }
        } catch (error) {
          console.error('压缩失败:', error)
          resolve(bestFile || lastCompressedFile || originalFile)
        }
      }

      tryCompress()
    },

    // 获取压缩后的文件大小
    getCompressedFileSize(compressedResult, originalFile, callback) {
      // #ifdef H5
      if (compressedResult instanceof Blob) {
        const reader = new FileReader()
        reader.onload = (e) => {
            const compressedFile = {
              ...originalFile,
            name: this.changeFileExtToJpg(originalFile.name), // 压缩后改为 .jpg
              path: e.target.result,
              tempFilePath: e.target.result,
            size: compressedResult.size,
            type: 'image/jpeg',
            originalSize: originalFile.size,
            compressRatio: ((originalFile.size - compressedResult.size) / originalFile.size * 100).toFixed(1),
            isCompressed: true,
            _blob: compressedResult
          }
          callback(compressedFile)
        }
        reader.onerror = () => {
          callback(originalFile)
        }
        reader.readAsDataURL(compressedResult)
      } else {
        callback(originalFile)
      }
      // #endif
      
      // #ifndef H5
      if (typeof compressedResult === 'string') {
        // 使用新的 API 代替过期的 uni.getFileInfo
        const fileSystemManager = uni.getFileSystemManager()
        fileSystemManager.getFileInfo({
          filePath: compressedResult,
          success: (fileInfo) => {
            const compressedFile = {
              ...originalFile,
              name: this.changeFileExtToJpg(originalFile.name), // 压缩后改为 .jpg
              path: compressedResult,
              tempFilePath: compressedResult,
              size: fileInfo.size,
              type: 'image/jpeg',
              originalSize: originalFile.size,
              compressRatio: ((originalFile.size - fileInfo.size) / originalFile.size * 100).toFixed(1),
              isCompressed: true
            }
            callback(compressedFile)
          },
          fail: () => {
            callback(originalFile)
          }
        })
      } else {
        callback(originalFile)
      }
      // #endif
    },

    // 处理压缩结果（用于质量压缩模式）
    handleCompressResult(originalFile, originalPath, compressedPaths, resolve) {
      if (!compressedPaths || compressedPaths.length === 0) {
        resolve(originalFile)
        return
      }

      const compressedResult = compressedPaths[0]
      
      this.getCompressedFileSize(compressedResult, originalFile, (compressedFile) => {
        // 检查压缩效果
        if (compressedFile.size >= originalFile.size * 0.9) {
          resolve(originalFile)
          return
        }

        resolve(compressedFile)
      })
    },
    
    // 初始化上传管理器
    initUploadManager() {
      this.uploadManager = createUploadManager({
        allowedTypes: this.allowedTypes,
        maxFileSize: this.dynamicMaxFileSize,
        maxCount: this.maxCount,
        chunkSize: this.chunkSize,
        autoUpload: this.autoUpload,
        currentCount: () => this.completedFiles.length,
        queueCount: () => this.allFiles.filter(f => f.status !== 'completed').length,
        disabled: () => this.disabled,
        isUploading: () => this.isUploading,
        canAddMore: () => this.completedFiles.length < this.maxCount
      })
    },

    // 数据格式转换工具函数
    normalizeModelValue(value) {
      if (!value) return []
      
      // 如果已经是数组，直接返回
      if (Array.isArray(value)) {
        return value.map(img => {
          if (typeof img === 'string') {
            // 如果数组元素是字符串，转换为对象
            return {
              id: generateFileId('image'),
              name: img.split('/').pop() || '图片',
              url: img,
              filePath: img,
              previewUrl: img
            }
          }
          return img
        })
      }
      
      // 如果是字符串，按逗号分割
      if (typeof value === 'string') {
        return value.split(',').filter(url => url.trim()).map(url => {
          const trimmedUrl = url.trim()
          return {
            id: generateFileId('image'),
            name: trimmedUrl.split('/').pop() || '图片',
            url: trimmedUrl,
            filePath: trimmedUrl,
            previewUrl: trimmedUrl
          }
        })
      }
      
      return []
    },

    // 数据格式转换为输出格式
    formatOutputValue(files) {
      const completedFiles = files.filter(file => file.status === 'completed')
      
      // 如果原始输入是字符串，输出也是字符串
      if (typeof this.modelValue === 'string') {
        return completedFiles.map(file => file.url || file.filePath).join(',')
      }
      
      // 否则输出数组格式
      return completedFiles.map(file => ({
        id: file.id,
        name: file.name,
        size: file.size,
        url: file.url,
        filePath: file.filePath,
        previewUrl: file.previewUrl || file.url
      }))
    },

    // 初始化 modelValue
    initFromModelValue() {
      const normalizedFiles = this.normalizeModelValue(this.modelValue)
      const newCompletedFiles = normalizedFiles.map(img => ({
        id: img.id || generateFileId('image'),
        name: img.name,
        size: img.size || 0,
        filePath: img.filePath,
        url: img.url,
        previewUrl: img.previewUrl || img.url,
        status: 'completed'
      }))

      // 保留非completed状态的文件，替换completed状态的文件
      const nonCompletedFiles = this.allFiles.filter(file => file.status !== 'completed')
      this.allFiles = [...nonCompletedFiles, ...newCompletedFiles]
    },

    // 更新 modelValue
    updateModelValue() {
      const outputValue = this.formatOutputValue(this.allFiles)
      this.$emit('update:modelValue', outputValue)
    },

    // 点击上传区域
    handleClickUpload(event) {
      if (this.disabled || this.isUploading || this.completedFiles.length >= this.maxCount) {
        return
      }
      
      this.chooseImages()
    },

    // 选择图片
    chooseImages() {
      if (this.disabled || this.isUploading || this.completedFiles.length >= this.maxCount) {
        return
      }

      try {
        uni.chooseImage({
          count: this.maxCount - this.completedFiles.length,
          sizeType: ['original'], // 始终使用原图，避免系统自动压缩
          sourceType: ['album', 'camera'],
          success: (res) => {
            if (res.tempFiles && res.tempFiles.length > 0) {
              this.addFilesToQueue(res.tempFiles)
            }
          },
          fail: (err) => {
            // 只有在真正的错误时才显示toast，避免用户取消时的干扰
            if (err.errMsg !== 'chooseImage:fail cancel') {
              uni.showToast({
                title: '选择图片失败',
                icon: 'error'
              })
            }
          }
        })
      } catch (error) {
        uni.showToast({
          title: '图片选择器调用失败',
          icon: 'error'
        })
      }
    },

    // 添加文件到队列
    async addFilesToQueue(files) {
      if (!this.uploadManager) {
        this.initUploadManager()
      }

      const { validFiles, errors } = this.uploadManager.validator.validateFiles(files, this.allFiles.filter(f => f.status !== 'completed'))

      // 显示错误信息
      errors.forEach(error => {
        uni.showToast({
          title: error,
          icon: 'error',
          duration: 3000
        })
      })

      // 处理有效文件
      const processedFiles = []
      for (const file of validFiles) {
        // 小程序环境下，生成基于时间戳的文件名
        // #ifndef H5
        if (!file.name || file.name.includes('tmp/')) {
          // 使用时间戳作为文件名
          const timestamp = Date.now()
          const ext = this.isImageFile(file.path) ? this.getFileExtension(file.path) : 'jpg'
          file.name = `image_${timestamp}.${ext}`
        }
        // #endif
        
        // H5 环境保持原始文件名
        // #ifdef H5
        if (!file.name) {
          file.name = `image_${Date.now()}.jpg`
        }
        // #endif
        
        let processedFile = file
        let originalSize = file.size
        let compressRatio = 0
        let isCompressed = false

        // 如果启用压缩且是图片文件
        if (this.enableCompress && ((file.type && file.type.startsWith('image/')) || this.isImageFile(file.name || file.path))) {
          try {
            // 发出压缩开始事件
            this.$emit('compress-start', { file: file })

            // 显示压缩提示
            const loadingTitle = this.compressMode === 'quality' 
              ? `正在质量压缩...` 
              : `正在压缩至${this.maxFileSizeKb}KB...`
            
            uni.showLoading({
              title: loadingTitle,
              mask: true
            })

            // 执行压缩
            processedFile = await this.compressImage(file)
            
            // 关闭加载提示
            uni.hideLoading()

            // 检查是否真的压缩了
            if (processedFile.isCompressed) {
              originalSize = processedFile.originalSize
              compressRatio = processedFile.compressRatio
              isCompressed = true

              // 发出压缩完成事件
              this.$emit('compress-end', { 
                originalFile: file,
                compressedFile: processedFile,
                originalSize,
                newSize: processedFile.size,
                compressRatio
              })

                const finalSizeKB = (processedFile.size / 1024).toFixed(1)
              const originalSizeKB = (originalSize / 1024).toFixed(1)
              
              if (this.compressMode === 'size') {
                const targetKB = this.maxFileSizeKb
                if (processedFile.size <= this.maxFileSizeKb * 1024) {
                  uni.showToast({
                    title: `✅ ${originalSizeKB}KB → ${finalSizeKB}KB (目标:${targetKB}KB)`,
                    icon: 'success',
                    duration: 2500
                  })
                } else {
                  uni.showToast({
                    title: `⚠️ ${originalSizeKB}KB → ${finalSizeKB}KB (目标:${targetKB}KB，已尽力)`,
                    icon: 'none',
                    duration: 3000
                  })
                }
              } else {
                uni.showToast({
                  title: `✅ ${originalSizeKB}KB → ${finalSizeKB}KB (压缩率:${compressRatio}%)`,
                  icon: 'success',
                  duration: 2500
                })
              }
            } else {
              uni.showToast({
                title: `图片无需压缩`,
                icon: 'none'
              })
            }
          } catch (error) {
            console.error('图片压缩失败:', error)
            uni.hideLoading()
            uni.showToast({
              title: `图片压缩失败，将使用原图`,
              icon: 'none'
            })
            processedFile = file // 使用原文件
          }
        }

        // 生成预览URL
        const previewUrl = await generatePreviewUrl(processedFile)

        // 确保文件名存在
        const fileName = processedFile.name || processedFile.path || processedFile.tempFilePath || `image_${Date.now()}.jpg`
        
        const fileObj = {
          id: generateFileId('image'),
          file: processedFile,
          name: fileName,
          size: processedFile.size,
          type: processedFile.type || this.getFileTypeFromName(fileName),
          status: 'waiting',
          progress: 0,
          uploadMethod: this.uploadManager.determineUploadMethod(processedFile),
          supportDirectUpload: false,
          errorMessage: '',
          filePath: '',
          uploadId: '',
          etags: [],
          previewUrl: previewUrl || (processedFile.path || processedFile.tempFilePath),
          // 压缩相关信息
          originalSize: originalSize,
          compressRatio: compressRatio,
          isCompressed: isCompressed
        }

        processedFiles.push(fileObj)
      }

      this.allFiles.push(...processedFiles)

      // 如果自动上传，立即开始上传
      if (this.autoUpload && processedFiles.length > 0) {
        this.$nextTick(() => {
          this.startUpload()
        })
      }
    },

    // 检查文件是否为图片文件
    isImageFile(fileName) {
      if (!fileName) return false
      
      try {
        const fileNameStr = String(fileName).toLowerCase()
        const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']
        const ext = fileNameStr.split('.').pop()
        return imageExtensions.includes(ext)
      } catch (error) {
        return false
      }
    },

    // 根据文件名获取文件类型
    getFileTypeFromName(fileName) {
      // 确保fileName是字符串类型且不为空
      if (!fileName || typeof fileName !== 'string') {
        return 'image/jpeg'
      }
      
      try {
        // 转换为字符串并获取扩展名
        const fileNameStr = String(fileName)
        const parts = fileNameStr.toLowerCase().split('.')
        const ext = parts.length > 1 ? parts.pop() : ''
        
        const mimeTypes = {
          'jpg': 'image/jpeg',
          'jpeg': 'image/jpeg',
          'png': 'image/png',
          'gif': 'image/gif',
          'bmp': 'image/bmp',
          'webp': 'image/webp'
        }
        
        return mimeTypes[ext] || 'image/jpeg'
      } catch (error) {
        return 'image/jpeg'
      }
    },

    // 移除图片（通过索引，用于网格模式）
    removeImage(index) {
      const completedFileIndex = this.allFiles.findIndex((file, i) => file.status === 'completed' && this.completedFiles.indexOf(file) === index)
      if (completedFileIndex !== -1) {
        this.allFiles.splice(completedFileIndex, 1)
      }
      this.$emit('remove', { index })
    },

    // 从统一列表中删除文件
    removeFileFromList(file, listIndex) {
      const fileIndex = this.allFiles.findIndex(f => f.id === file.id)
      if (fileIndex !== -1) {
        this.allFiles.splice(fileIndex, 1)
        this.$emit('remove', { index: fileIndex })
      }
    },

    // 开始上传
    async startUpload() {
      // 确保存储配置已加载
      if (this.storageConfigs.length === 0) {
        try {
          await this.loadStorageConfigs()
        } catch (error) {
          uni.showToast({
            title: '加载存储配置失败，请检查网络连接',
            icon: 'error'
          })
          return
        }
      }

      if (!this.selectedConfigId) {
        if (this.storageConfigs.length > 0) {
          this.selectedConfigId = await this.getDefaultConfigId(this.storageConfigs)
        } else {
          uni.showToast({
            title: '没有可用的存储配置，请先在系统管理中配置存储',
            icon: 'error'
          })
          return
        }
      }

      const waitingFiles = this.allFiles.filter(file => file.status === 'waiting')
      if (waitingFiles.length === 0) {
        uni.showToast({
          title: '没有文件需要上传',
          icon: 'none'
        })
        return
      }

      // 开始上传流程
      this.isUploading = true
      uni.showLoading({
        title: '上传中...'
      })
      
      try {
        const config = this.storageConfigs.find(c => c.configId === this.selectedConfigId)

        for (const fileObj of waitingFiles) {
          try {
            fileObj.status = 'uploading'
            fileObj.progress = 0
            fileObj.errorMessage = ''

            const onProgress = (progress) => {
              this.$emit('upload-progress', { file: fileObj, progress })
            }

            if (fileObj.uploadMethod === 'chunk') {
              await this.uploadManager.uploadFileWithChunks(
                fileObj, config, this.selectedConfigId, this.uploadDir, this.chunkSize, onProgress
              )
            } else {
              await this.uploadManager.uploadFileNormally(
                fileObj, config, this.selectedConfigId, this.uploadDir, onProgress
              )
            }

            fileObj.status = 'completed'
            fileObj.progress = 100

            // 使用后端返回的URL，如果没有则尝试获取
            let url = fileObj.url
            if (!url) {
              url = await this.uploadManager.getImageUrl(fileObj.filePath, this.selectedConfigId)
              fileObj.url = url
            }

            // 更新文件对象的完整信息
            fileObj.filePath = fileObj.filePath
            fileObj.url = url

            this.$emit('upload-success', {
              file: fileObj,
              image: fileObj
            })

          } catch (error) {
            fileObj.status = 'error'
            fileObj.errorMessage = error.message || '上传失败'

            this.$emit('upload-error', {
              file: fileObj,
              error: error
            })
          }
        }

        uni.showToast({
          title: '上传完成',
          icon: 'success'
        })

      } catch (error) {
        uni.showToast({
          title: '上传过程中发生错误：' + error.message,
          icon: 'error'
        })
      } finally {
        uni.hideLoading()
        this.isUploading = false
      }
    },

    // 重试上传
    retryUpload(fileObj) {
      const fileIndex = this.allFiles.findIndex(f => f.id === fileObj.id)
      if (fileIndex !== -1) {
        this.allFiles[fileIndex].status = 'waiting'
        this.allFiles[fileIndex].progress = 0
        this.allFiles[fileIndex].errorMessage = ''
      }
    },

    // 取消上传
    async cancelUpload() {
      try {
        const res = await new Promise((resolve) => {
          uni.showModal({
            title: '提示',
            content: '确定要取消上传吗？',
            success: resolve
          })
        })

        if (!res.confirm) return

        this.isUploading = false

        // 重置所有上传中的文件状态
        this.allFiles.forEach(file => {
          if (file.status === 'uploading') {
            file.status = 'waiting'
            file.progress = 0
          }
        })

        uni.showToast({
          title: '已取消上传',
          icon: 'none'
        })
      } catch (error) {
        // 用户取消确认
      }
    },

    // 清空全部
    clearAll() {
      this.allFiles = []
    },

    // 从统一列表中预览图片
    previewImageFromList(file, listIndex) {
      if (file.status === 'completed') {
        const completedIndex = this.completedFiles.findIndex(img => img.id === file.id)
        if (completedIndex !== -1) {
          this.previewImage(file, completedIndex)
        }
      }
    },

    // 预览图片
    previewImage(image, index) {
      this.currentPreviewIndex = index
      this.showPreview = true
      this.$emit('preview', { image, index })
    },

    // 关闭预览
    closePreview() {
      this.showPreview = false
      this.currentPreviewIndex = 0
    },

    // 预览切换
    onPreviewChange(index) {
      this.currentPreviewIndex = index
    },

    // 复制路径
    copyPath(path) {
      this.uploadManager.copyPath(path)
    },

    // 下载图片
    async downloadImage(image) {
      try {
        uni.downloadFile({
          url: image.url || image.filePath,
          success: (res) => {
            if (res.statusCode === 200) {
              uni.saveImageToPhotosAlbum({
                filePath: res.tempFilePath,
                success: () => {
                  uni.showToast({
                    title: '保存成功',
                    icon: 'success'
                  })
                },
                fail: () => {
                  uni.showToast({
                    title: '保存失败',
                    icon: 'error'
                  })
                }
              })
            }
          },
          fail: () => {
            uni.showToast({
              title: '下载失败',
              icon: 'error'
            })
          }
        })
      } catch (error) {
        uni.showToast({
          title: '下载失败: ' + error.message,
          icon: 'error'
        })
      }
    },

    // 加载存储配置
    async loadStorageConfigs() {
      try {
        this.storageConfigs = await this.uploadManager.loadStorageConfigs()

        if (!this.selectedConfigId && this.storageConfigs.length > 0) {
          this.selectedConfigId = await this.getDefaultConfigId(this.storageConfigs)
        }

        // 更新动态文件大小限制
        this.updateDynamicMaxFileSize()
      } catch (error) {
        // 静默处理
      }
    },

    // 获取默认配置ID
    async getDefaultConfigId(configs) {
      if (!configs || configs.length === 0) {
        return null
      }
      
      // 优先使用默认配置
      const defaultConfig = configs.find(config => config.isDefault === 1)
      if (defaultConfig) {
        return defaultConfig.configId
      }
      
      // 如果没有默认配置，使用第一个可用配置
      return configs[0].configId
    },

    // 更新动态文件大小限制
    updateDynamicMaxFileSize() {
      if (this.selectedConfigId && this.storageConfigs.length > 0) {
        const config = this.storageConfigs.find(c => c.configId === this.selectedConfigId)
        if (config && config.maxFileSize) {
          // 将字节转换为MB
          this.dynamicMaxFileSize = Math.floor(config.maxFileSize / (1024 * 1024))
        }
      }
      // 重新初始化上传管理器以使用新的文件大小限制
      this.initUploadManager()
    }
  }
}
</script>

<style lang="scss" scoped>
.image-upload {
  .upload-zone {
    border: 2px dashed #dcdfe6;
    border-radius: 12rpx;
    padding: 40rpx;
    text-align: center;
    background-color: #fafafa;
    transition: all 0.3s ease;

    &:active:not(.upload-zone--disabled) {
      border-color: #409eff;
      background-color: #f0f9ff;
    }

    &--disabled {
      opacity: 0.6;
    }

    &--grid {
      padding: 0;
    }

    &__content {
      display: flex;
      flex-direction: column;
      align-items: center;
    }

    &__text {
      margin-top: 32rpx;
    }

    &__title {
      font-size: 32rpx;
      color: #303133;
      margin-bottom: 16rpx;
      display: block;
    }

    &__subtitle {
      font-size: 28rpx;
      color: #909399;
      margin: 8rpx 0;
      display: block;
    }
  }

  // 网格布局
  .image-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240rpx, 1fr));
    gap: 24rpx;
    padding: 24rpx;

    .image-item {
      position: relative;

      .image-wrapper {
        position: relative;
        width: 100%;
        height: 240rpx;
        border-radius: 12rpx;
        overflow: hidden;
        border: 1px solid #dcdfe6;
        background-color: #f5f7fa;

        .image-preview {
          width: 100%;
          height: 100%;
          display: block;
        }

        .image-overlay {
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          background: rgba(0, 0, 0, 0.5);
          display: flex;
          align-items: center;
          justify-content: center;
          opacity: 0;
          transition: opacity 0.3s ease;

          .image-actions {
            display: flex;
            gap: 16rpx;

            .action-btn {
              width: 64rpx;
              height: 64rpx;
              border-radius: 50%;
              display: flex;
              align-items: center;
              justify-content: center;
              transition: all 0.3s ease;

              &.action-preview {
                background: rgba(64, 158, 255, 0.8);
              }

              &.action-download {
                background: rgba(103, 194, 58, 0.8);
              }

              &.action-delete {
                background: rgba(245, 108, 108, 0.8);
              }
            }
          }
        }

        &:active .image-overlay {
          opacity: 1;
        }

        .image-name {
          position: absolute;
          bottom: 0;
          left: 0;
          right: 0;
          background: rgba(0, 0, 0, 0.7);
          color: white;
          padding: 8rpx;
          font-size: 24rpx;
          text-align: center;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
        }
      }
    }

    .upload-placeholder {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      height: 240rpx;
      border: 2px dashed #dcdfe6;
      border-radius: 12rpx;
      background-color: #fafafa;
      transition: all 0.3s ease;

      &:active {
        border-color: #409eff;
        background-color: #f0f9ff;
      }
      
      .upload-btn {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        width: 100%;
        height: 100%;
        background: transparent;
        border: none;
        outline: none;
        
        &:disabled {
          opacity: 0.6;
        }
      }
      
      .upload-text {
        font-size: 24rpx;
        color: #909399;
        margin-top: 16rpx;
      }
    }
  }

  // 列表布局
  .unified-file-list {
    margin-top: 32rpx;

    .list-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 24rpx 32rpx;
      background-color: #f5f7fa;
      border-radius: 12rpx 12rpx 0 0;
      border: 1px solid #e4e7ed;
      font-weight: 500;
      color: #303133;
      font-size: 28rpx;

      .header-actions {
        display: flex;
        gap: 16rpx;
      }
    }

    .file-section {
      border: 1px solid #e4e7ed;
      border-top: none;
      border-radius: 0 0 12rpx 12rpx;

      .file-list {
        .file-item {
          display: flex;
          border-bottom: 1px solid #f0f0f0;
          padding: 32rpx;
          gap: 32rpx;
          align-items: flex-start;

          &:last-child {
            border-bottom: none;
          }

          &.file-item--error {
            background-color: #fef0f0;
          }

          // 左侧图片区域
          .file-image {
            flex-shrink: 0;

            .image-preview-container {
              position: relative;
              width: 240rpx;
              height: 240rpx;
              border-radius: 16rpx;
              overflow: hidden;
              border: 1px solid #e4e7ed;
              background-color: #f8f9fa;

              .preview-image {
                width: 100%;
                height: 100%;
                display: block;

                &.clickable {
                  transition: transform 0.2s ease;

                  &:active {
                    transform: scale(1.05);
                  }
                }
              }

              .image-placeholder {
                width: 100%;
                height: 100%;
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                color: #c0c4cc;

                .placeholder-text {
                  font-size: 24rpx;
                  margin-top: 16rpx;
                }
              }

              .upload-overlay {
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background-color: rgba(0, 0, 0, 0.6);
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                color: white;

                progress {
                  width: 160rpx;
                  margin-bottom: 24rpx;
                }

                .upload-progress {
                  font-size: 28rpx;
                  font-weight: 500;
                }
              }
            }
          }

          // 右侧内容区域
          .file-content {
            flex: 1;
            display: flex;
            flex-direction: column;
            min-height: 240rpx;

            .file-info {
              flex: 1;
              margin-bottom: 24rpx;

              .file-name {
                font-size: 32rpx;
                font-weight: 500;
                color: #303133;
                margin-bottom: 16rpx;
                line-height: 1.4;
                word-break: break-word;
              }

              .file-meta {
                .meta-item {
                  display: flex;
                  align-items: center;
                  margin-bottom: 8rpx;
                  font-size: 26rpx;

                  .meta-label {
                    color: #909399;
                    min-width: 80rpx;
                    flex-shrink: 0;
                  }

                  .meta-value {
                    color: #606266;

                    &.file-path {
                      font-family: monospace;
                      word-break: break-all;
                    }
                  }

                  .direct-tag {
                    margin-left: 16rpx;
                    padding: 4rpx 12rpx;
                    background: #f0f9ff;
                    color: #67c23a;
                    border-radius: 6rpx;
                    font-size: 20rpx;
                  }

                  .compress-info {
                    color: #67c23a;
                    font-size: 22rpx;
                    font-weight: 500;
                    margin-left: 8rpx;
                  }
                }
              }
            }

            .file-actions {
              display: flex;
              justify-content: space-between;
              align-items: center;
              margin-top: auto;

              .status-section {
                flex-shrink: 0;

                .status-tag {
                  padding: 8rpx 16rpx;
                  border-radius: 12rpx;
                  font-size: 24rpx;
                  
                  &.waiting {
                    background: #f4f4f5;
                    color: #909399;
                  }
                  
                  &.uploading {
                    background: #fdf6ec;
                    color: #e6a23c;
                  }
                  
                  &.completed {
                    background: #f0f9ff;
                    color: #67c23a;
                  }
                  
                  &.error {
                    background: #fef0f0;
                    color: #f56c6c;
                  }
                  
                  &.compressed {
                    background: #f0f9ff;
                    color: #67c23a;
                    margin-left: 8rpx;
                  }
                }
              }

              .action-buttons {
                display: flex;
                gap: 16rpx;
                flex-wrap: wrap;
              }
            }

            .error-message {
              margin-top: 24rpx;

              .error-alert {
                display: flex;
                align-items: center;
                padding: 16rpx;
                background: #fef0f0;
                border: 1px solid #fbc4c4;
                border-radius: 8rpx;
                font-size: 24rpx;
                color: #f56c6c;

                text {
                  margin-left: 8rpx;
                }
              }
            }
          }
        }
      }
    }
  }

  // 按钮样式
  .btn {
    padding: 12rpx 24rpx;
    border-radius: 8rpx;
    font-size: 24rpx;
    border: none;
    color: #fff;

    &.btn-primary {
      background: #409eff;
    }

    &.btn-danger {
      background: #f56c6c;
    }

    &.btn-text {
      background: none;
      color: #409eff;
    }

    &.btn-sm {
      padding: 8rpx 16rpx;
      font-size: 22rpx;
    }
  }

  .btn-sm {
    padding: 8rpx 16rpx;
    border-radius: 6rpx;
    font-size: 22rpx;
    border: none;
    color: #fff;

    &.btn-primary {
      background: #409eff;
    }

    &.btn-info {
      background: #909399;
    }

    &.btn-success {
      background: #67c23a;
    }

    &.btn-warning {
      background: #e6a23c;
    }

    &.btn-danger {
      background: #f56c6c;
    }
  }
}
</style>

